package com.example.mediamoudle.systime.base

import android.media.MediaCodec
import android.media.MediaFormat
import java.io.File
import java.nio.ByteBuffer

/**
 * 定义基础解码器
 */

abstract class BaseDecoder(val mVideoPath: String) : IDecoder {
    /** 线程有关 **/

    /**
     * 解码器是否在运行
     */

    private var mIsRunning = true

    /**
     * 线程等待锁
     */
    private val mLock = Object()

    /**
     * 是否可以进入解码
     */
    private var mReadyForDecode = false

    /** 解码有关 **/
    /**
     * 音视频解码器
     */
    protected var mCodec: MediaCodec? = null

    /**
     * 音视频数据读取器
     */
    protected var mExtractor: IExtractor? = null

    /**
     * 解码输入缓冲区
     */
    protected var mInputBuffers: Array<ByteBuffer>? = null

    /**
     * 解码输出缓冲区
     */
    protected var mOutputBuffers: Array<ByteBuffer>? = null

    /**
     * 解码数据信息
     */
    private var mBufferInfo = MediaCodec.BufferInfo()

    private var mState = DecodeState.STOP

    protected var mStateListener: IDecoderStateListener? = null


    /**
     * 流数据是否结束
     */
    private var mIsEOS = false

    private var mVideoWidth = 0

    protected var mVideoHeight = 0

    private var mDuration: Long = 0

    private var mStartPos: Long = 0

    private var mEndPos: Long = 0

    /***开始解码时间，用于音视频同步*/
    private var mStartTimeForSync = -1L

    /**是否需要音视频渲染同步**/
    private var mSyncRender = true


    override fun run() {
        if (mState == DecodeState.STOP) mState = DecodeState.START
        mStateListener?.decoderPrepare(this)

        /**初始化解码器**/
        if (!initMediaCodec()) return

        try {
            while (mIsRunning) {
                if (mState != DecodeState.START && mState != DecodeState.DECODING && mState != DecodeState.SEEKING) {
                    waitDecode()
                    /**恢复同步的起始时间，减去等待时长**/
                    mStartTimeForSync = System.currentTimeMillis() - getCurrentFrameTimeStamp()
                }
                /**退出解码**/
                if (!mIsRunning || mState == DecodeState.STOP) {
                    mIsRunning = false
                    break
                }
                if (mStartTimeForSync == -1L) {
                    mStartTimeForSync = System.currentTimeMillis()
                }
                //如果没有解码完成,则将数据流进行存入缓冲区
                if (!mIsEOS) {
                    mIsEOS = pushBufferToDecoder()
                }
                //将解码后的数据从缓冲区取出来
                val index = pullBufferFromDecoder()
                if (index>0){
                    if (mSyncRender && mState == DecodeState.DECODING){
                        sleepRender()
                    }
                    if (mSyncRender){
                        render(mOutputBuffers!![index],mBufferInfo)
                    }
                    val frame = Frame()
                    frame.buffer = mOutputBuffers!![index]
                    frame.setBufferInfo(mBufferInfo)
                    mStateListener?.decodeOneFrame(this,frame)

                    //释放缓冲区
                    mCodec!!.releaseOutputBuffer(index,true)
                    if (mState == DecodeState.START){
                        mState = DecodeState.PAUSE
                    }
                }
                if (mBufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM){
                    mState = DecodeState.FINISH
                    mStateListener?.decoderFinish(this)
                }
                

            }

        } catch (e: Exception) {
          e.printStackTrace()
        }finally {
            doneDecode()
            release()
        }

    }
    private fun release(){
        try {
            mState = DecodeState.STOP
            mIsEOS = false
            mExtractor?.stop()
            mCodec?.stop()
            mCodec?.release()
            mStateListener?.decoderDestroy(this)
        }catch (e:Exception){

        }
    }



    private fun sleepRender() {
        val passTime = System.currentTimeMillis() - mStartTimeForSync
        val currentTime = getCurrentFrameTimeStamp()
        if (currentTime>passTime){
            Thread.sleep(currentTime - passTime)
        }
    }

    /**
     * 将解码后的视频流从缓冲区取出来
     */
    private fun pullBufferFromDecoder(): Int {
        when(val  index = mCodec!!.dequeueOutputBuffer(mBufferInfo,1000)){
            MediaCodec.INFO_OUTPUT_FORMAT_CHANGED ->{} //输出格式发送改变
            MediaCodec.INFO_TRY_AGAIN_LATER ->{}  //暂时没有可用数据
            MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED ->{ //输出缓冲数据发生改变
                mOutputBuffers = mCodec!!.outputBuffers
            }else ->{
                return index
            }
        }
        return  -1
    }

    /**
     * 将解码出来的视频流放入缓冲
     */
    private fun pushBufferToDecoder(): Boolean {
        val inputBufferIndex = mCodec!!.dequeueInputBuffer(1000)
        var isEndOfStream = false
        if (inputBufferIndex >= 0) {
            val mInputBuffer = mInputBuffers!![inputBufferIndex]
            val sampleSize = mExtractor!!.readBuffer(mInputBuffer)
            //解码完成
            if (sampleSize < 0) {
                mCodec!!.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
                isEndOfStream = true

            } else {
               mCodec!!.queueInputBuffer(inputBufferIndex,0,sampleSize,mExtractor!!.getCurrentTimestamp(),0)
            }
        }
        return isEndOfStream
    }

    private fun initMediaCodec(): Boolean {
        if (mVideoPath.isNullOrEmpty() || !File(mVideoPath).exists()) {
            mStateListener?.decoderError(this, "该文件不存在")
            return false
        }
        if (!check()) return false
        /***初始化媒体提取器*/
        mExtractor = initExtractor(mVideoPath)
        if (mExtractor == null || mExtractor!!.getFormat() == null) return false
        //初始化参数
        if (!initParams()) return false

        //初始化渲染器
        if (!initRender()) return false

        //初始化解码器
        if (!initCodec()) return false
        return true
    }

    private fun initCodec(): Boolean {
        try {
            val type = mExtractor!!.getFormat()!!.getString(MediaFormat.KEY_MIME)
            mCodec = type?.let { MediaCodec.createDecoderByType(it) }
            if (!configCodec(mCodec!!, mExtractor!!.getFormat()!!)) {
                waitDecode()
            }
            mCodec!!.start()
            mInputBuffers = mCodec?.inputBuffers
            mOutputBuffers = mCodec?.outputBuffers

        } catch (e: Exception) {
            return false
        }
        return true
    }

    private fun waitDecode() {
        try {
            if (mState == DecodeState.PAUSE) {
                mStateListener?.decoderPause(this)
            }
            synchronized(mLock) {
                mLock.wait()
            }
        } catch (e: Exception) {

        }
    }

    private fun initParams(): Boolean {
        try {
            val format = mExtractor!!.getFormat()!!
            mDuration = format.getLong(MediaFormat.KEY_DURATION) / 1000
            if (mEndPos == 0L) mEndPos = mDuration
            initSpecParams(mExtractor!!.getFormat()!!)
        } catch (e: Exception) {
            return false
        }
        return true
    }

    override fun getCurrentFrameTimeStamp(): Long {
        return mBufferInfo.presentationTimeUs / 1000
    }

    override fun pause() {
        mState = DecodeState.DECODING
    }

    override fun goOn() {
        mState = DecodeState.DECODING
        notifyDecode()
    }

    protected fun notifyDecode() {
        synchronized(mLock){
            mLock.notifyAll()
        }
        if (mState == DecodeState.DECODING){
            mStateListener?.decoderRunning(this)
        }
    }

    override fun seekTo(pos: Long): Long {
        return 0
    }

    override fun seekAndPlay(pos: Long): Long {
        return 0
    }

    override fun stop() {
        mState = DecodeState.STOP
        mIsRunning = false
        notifyDecode()
    }

    override fun isDecoding(): Boolean {
        return mState == DecodeState.DECODING
    }

    override fun isSeekIng(): Boolean {
        return mState == DecodeState.SEEKING
    }

    override fun isStop(): Boolean = mState == DecodeState.STOP

    override fun setStateListener(listener: IDecoderStateListener?) {
        mStateListener = listener
    }

    override fun getWidth(): Int = mVideoWidth
    override fun getHeight(): Int = mVideoHeight
    override fun getDuration(): Long = mDuration
    override fun getRotationAngle(): Int = 0
    override fun getMediaFormat(): MediaFormat? {
        return mExtractor?.getFormat()
    }

    override fun getTrack(): Int {
        return 0
    }

    override fun getFilePath(): String {
        return mVideoPath
    }

    override fun withoutSync(): IDecoder {
        mSyncRender = false
        return this
    }

    /**
     * 结束解码
     */
    abstract fun doneDecode()
    /**
     * 渲染
     */
    abstract fun render(outputBuffer: ByteBuffer,
                        bufferInfo: MediaCodec.BufferInfo)
    /**
     * 配置解码器
     */
    abstract fun configCodec(codec: MediaCodec, format: MediaFormat): Boolean

    /**
     * 初始化子类自己特有的参数
     */
    abstract fun initSpecParams(format: MediaFormat)

    /**
     * 初始化渲染器
     */
    abstract fun initRender(): Boolean

    /**
     * 检查子类参数
     */
    abstract fun check(): Boolean

    /**
     * 初始化数据提取器
     */
    abstract fun initExtractor(path: String): IExtractor

}